package zdream.control.world.buff;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;

import zdream.control.world.Box;
import zdream.control.world.MFoe;
import zdream.control.world.MFoeBuff;
import zdream.control.world.MHookType;
import zdream.control.world.MHooks;
import zdream.control.world.Ticker;

import static zdream.control.world.MFoe.*;

/**
 * <p>敌人 AI 模块, 通过 Stage 来决定当前行动
 * </p>
 * 
 * @author Zdream
 * @since 0.0.1
 * @date 2022-06-11 (created)
 */
public class StageModule extends MFoeBuff {
	public static final String CATEGORY = "stage";

	public StageModule(MFoe foe) {
		super(foe);
	}

	@Override
	public String getCategory() {
		return CATEGORY;
	}

	public void init() {
		onHook(MHookType.TICK)
				.withPriority(-10)
				.handle(this::tick)
				.buildAndSave();
	}

	public void tick() {
		Ticker ticker = foe.world.ticker;
		stageDuration = ticker.activeTime - this.stageStartTime;
		BiConsumer<StageModule, Ticker> handler = stages.get(currentStage);
		handler.accept(this, ticker);
	}

	/* **********
	 *   阶段   *
	 ********** */
	public static final String STAGE_NONE = "none";
	public String currentStage = STAGE_NONE;
	public String initStage = STAGE_NONE;
	/**
	 * 上次 stage 修改时的 activeTime
	 */
	public long stageStartTime = 0;
	public long stageDuration = 0;

	public Map<String, BiConsumer<StageModule, Ticker>> stages = new HashMap<>();
	{
		registerStage(STAGE_NONE, (m, t) -> changeStageTo(initStage));
	}

	public void registerStage(String name, BiConsumer<StageModule, Ticker> handler) {
		this.stages.put(name, handler);
	}

	public boolean changeStageTo(String stageName) {
		final MHooks.FoeStageUpdateHookContext context = foe.triggerHook(MHookType.UPDATE_STAGE).as();
		context.withFoe(foe).withChanged(currentStage, stageName).run();

		if (context.aborted) {
			System.out.printf("%s 的状态转变 (%s->%s) 被 %s 阻止了%n", foe, currentStage, stageName, context.abortBy);
			return false;
		}

		currentStage = stageName;
		stageStartTime = foe.world.ticker.activeTime;
		stageDuration = 0;
		return true;
	}

	private static void doNothing(StageModule s, Ticker ticker) {}

	/* **********
	 * 工具方法 *
	 ********** */
	private static final List<MFoe> tmps = new ArrayList<>();

	/**
	 * 查找特定区域的敌人.
	 * 规则是, side 和自己不同; foe.type 是 foe/elite/leader
	 */
	public void findEnemy(List<MFoe> array, float x, float y, float width, float height) {
		foe.world.findOverlapFoeAndPutInList(array, x, y, width, height);
		filterEntityEnemies(array);
	}

	public void findAllEnemiesInRoom(List<MFoe> array) {
		array.addAll(foe.world.foes);
		filterEntityEnemies(array);
	}

	private void filterEntityEnemies(List<MFoe> array) {
		if (array.isEmpty()) {
			return;
		}

		byte selfSide = foe.side;
		array.removeIf(foe -> foe.side == selfSide);
		array.removeIf(foe -> !Objects.equals(foe.type, TYPE_LEADER)
				&& !Objects.equals(foe.type, TYPE_FOE)
				&& !Objects.equals(foe.type, TYPE_ELITE));
	}

	/**
	 * 在指定区域内找和自己 (box 中点) 最近的敌人.
	 * 敌人的规则是, camp 和自己不同; foe.type 是 foe/elite/leader
	 */
	public MFoe findNearestEnemy(float x, float y, float width, float height) {
		tmps.clear();
		findEnemy(tmps, x, y, width, height);
		if (tmps.isEmpty()) {
			return null;
		}

		MFoe nearestEnemy = chooseNearestEnemy(tmps);
		tmps.clear();
		return nearestEnemy;
	}

	public MFoe findNearestEnemyInRoom() {
		tmps.clear();
		findAllEnemiesInRoom(tmps);
		if (tmps.isEmpty()) {
			return null;
		}

		MFoe nearestEnemy = chooseNearestEnemy(tmps);
		tmps.clear();
		return nearestEnemy;
	}

	private MFoe chooseNearestEnemy(List<MFoe> array) {
		Box box = foe;
		float cx = box.getCenterX(), cy = box.getCenterY();
		MFoe nearestEnemy = null;
		double distance = 0;

		for (MFoe enemy : array) {
			double d = Math.hypot(box.getCenterX() - cx, box.getCenterY() - cy);
			if (nearestEnemy == null || d <= distance) {
				nearestEnemy = enemy;
				distance = d;
			}
		}
		return nearestEnemy;
	}

	/* **********
	 *   工厂   *
	 ********** */
	public static class StageModuleFactory {
		StageModule module;
		public StageModuleFactory(MFoe foe) {
			module = new StageModule(foe);
		}
		public StageModuleFactory defineStage(String stageName, BiConsumer<StageModule, Ticker> handler) {
			module.registerStage(stageName, handler);
			return this;
		}
		public StageModuleFactory defineInitStage(String stageName, BiConsumer<StageModule, Ticker> handler) {
			module.registerStage(stageName, handler);
			module.initStage = stageName;
			return this;
		}
		public StageModuleFactory defineDoNothingStage(String stageName) {
			return defineStage(stageName, StageModule::doNothing);
		}
		public StageModule create() {
			if (STAGE_NONE.equals(module.initStage)) {
				throw new IllegalArgumentException("init stage 不能为 none");
			}
			module.foe.addBuff(module);
			return module;
		}
	}
}
